package service

import (
	"cat_mouse/infra/common"
	"cat_mouse/infra/utils/log"
	"github.com/gorilla/websocket"
	"sync"
	"time"
)

// WsClient 定义websocket客户端连接信息
type WsClient struct {
	userId        string
	con           *websocket.Conn
	send          chan []byte
	readLimit     int64
	readDeadline  time.Time
	writeReadline time.Time
	stopOnce      sync.Once
}

type WsClientOpt func(*WsClient)

func WithConnOpt(conn *websocket.Conn) WsClientOpt {
	return func(client *WsClient) {
		client.con = conn
	}
}

func WithReadLimit(readLimit int64) WsClientOpt {
	return func(client *WsClient) {
		client.readLimit = readLimit
	}
}

func WithReadDeadline(readDeadline time.Time) WsClientOpt {
	return func(client *WsClient) {
		client.readDeadline = readDeadline
	}
}

func WithWriteDeadline(writeDeadline time.Time) WsClientOpt {
	return func(client *WsClient) {
		client.writeReadline = writeDeadline
	}
}

func WithUserId(userId string) WsClientOpt {
	return func(client *WsClient) {
		client.userId = userId
	}
}

func NewWsClient(opts ...WsClientOpt) *WsClient {
	client := &WsClient{
		send:          make(chan []byte, 256),
		readDeadline:  time.Now().Add(common.PongWait),
		writeReadline: time.Now().Add(common.WriteWait),
		readLimit:     common.MaxMessageSize,
		stopOnce:      sync.Once{},
	}
	for _, opt := range opts {
		opt(client)
	}
	_ = client.con.SetReadDeadline(client.readDeadline)
	_ = client.con.SetWriteDeadline(client.writeReadline)
	client.con.SetReadLimit(client.readLimit)
	return client
}

// Read 读取客户端发送过来的消息并转发到业务管道
func (ws *WsClient) Read(dataCh chan interface{}) {
	defer ws.Stop()
	for {
		_, data, err := ws.con.ReadMessage()
		if err != nil {
			log.Logger.Error(err.Error())
			break
		}
		dataCh <- data
	}
}

func (ws *WsClient) Write() {
	ticker := time.NewTicker(common.PingPeriod)
	defer ticker.Stop()
	defer close(ws.send)
	defer ws.Stop()
	for {
		select {
		case message, ok := <-ws.send:
			if !ok {
				_ = ws.con.WriteMessage(websocket.CloseMessage, []byte{})
				return
			}
			if err := ws.con.WriteMessage(websocket.TextMessage, message); err != nil {
				log.Logger.Error(err.Error())
				return
			}
		case <-ticker.C:
			_ = ws.con.WriteMessage(websocket.CloseMessage, []byte{})
			return
		}
	}
}

func (ws *WsClient) Stop() {
	ws.stopOnce.Do(func() {
		_ = ws.con.Close()
	})
}
